#include <QPainter>
#include <QMessageBox>
#include <QGraphicsSceneMouseEvent>
#include <QString>
#include <QWidget>
#include <QImage>
#include <QFile>

#include "tiledesignerscene.h"

TileDesignerScene::TileDesignerScene(QObject *parent)
        : QGraphicsScene(parent)
{
    editing = false;
    adding = false;
    currentLayer = 1;
}

void TileDesignerScene::drawForeground(QPainter *painter, const QRectF &)
{
    if (editing)
    {
        for (qint32 i = 0; i <= mapSizeX * tileSizeX; i+= tileSizeX)
        {
            QPointF start(i, 0);
            QPointF end(i, mapSizeY * tileSizeY);

            painter->drawLine(start, end);
        }

        for (qint32 j = 0; j<= mapSizeY * tileSizeY; j+= tileSizeY)
        {
            QPointF start(0, j);
            QPointF end(mapSizeX * tileSizeX, j);

            painter->drawLine(start, end);
        }
    }
}

void TileDesignerScene::drawBackground(QPainter *painter, const QRectF &rect)
{
    if (editing)
    {
        painter->setCompositionMode(QPainter::CompositionMode_SourceOver);

        for (qint32 layer = 0; layer < layers->count(); layer++)
        {
            if ((*layers)[layer].visible())
            {
                for (qint32 i = 0; i < mapSizeX; i++)
                {
                    for (qint32 j = 0; j < mapSizeY; j++)
                    {
                        QRect destRect = tileRect(i, j);
                        if (rect.intersects(destRect))
                        {
                            drawTile(layer, painter, i, j, destRect);
                        }
                    }
                }
            }
        }
    }
}

 QRect TileDesignerScene::tileRect(int x, int y) const
 {
     return QRect(x * tileSizeX, y * tileSizeY, tileSizeX, tileSizeY);
 }

 QRect TileDesignerScene::tileRect(int tileNum) const
 {
     qint32 numHTiles = tileSet->width() / tileSizeX;
     return tileRect(tileNum % numHTiles, tileNum / numHTiles);
 }

void TileDesignerScene::drawTile(qint16 layer, QPainter *painter, qint32 &i, qint32 &j, QRect &destRect)
{
    QVector<QVector<int> > *tiles = (*layers)[layer].data();

    if (tiles->at(i).at(j) == -1)
    {
        if (layer == 0)
        {
            painter->fillRect(destRect, defaultColour);
        }
        else
        {
            painter->fillRect(destRect, Qt::transparent);
        }
    }
    else
    {
        painter->drawPixmap(destRect, *tileSet, tileRect(tiles->at(i).at(j)));
    }
}

void TileDesignerScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
    if (editing)
    {
        if (mouseEvent->button() == Qt::LeftButton)
        {
            processTile(currentLayer, mouseEvent->scenePos(), selectedTile);
            adding = true;
        }
        else if (mouseEvent->button() == Qt::RightButton)
        {
            processTile(currentLayer, mouseEvent->scenePos(), -1);
        }
    }
}

void TileDesignerScene::processTile(qint16 layer, QPointF tileAt, qint32 tileNumber)
{
    if (sceneRect().contains(tileAt))
    {
        TileInfo tileInfo = getTileInfo(tileAt);

        (*(*layers)[layer].data())[tileInfo.tileX][tileInfo.tileY] = tileNumber;
        invalidate(QRectF(), BackgroundLayer);
    }
}

void TileDesignerScene::mouseMoveEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
    if (editing && adding)
    {
        processTile(currentLayer, mouseEvent->scenePos(), selectedTile);
    }
}

void TileDesignerScene::mouseReleaseEvent(QGraphicsSceneMouseEvent *)
{
    if (editing)
    {
        adding = false;
    }
}

TileInfo TileDesignerScene::getTileInfo(QPointF position)
{
    TileInfo tileInfo;
    tileInfo.tileX = (position.x() - 1) / tileSizeX;
    tileInfo.tileY = (position.y() - 1) / tileSizeY;

    return tileInfo;
}

QImage TileDesignerScene::save()
{
    editing = false;
    qint32 sizeX = mapSizeX * tileSizeX;
    qint32 sizeY = mapSizeY * tileSizeY;

    QImage image(sizeX, sizeY, QImage::Format_RGB32);
    QPainter painter(&image);

    QRectF source(QPointF(0, 0), QSizeF(sizeX, sizeY));

    render(&painter, QRectF(), source);

    editing = true;

    return image;
}

QPixmap *setTransparentColour(QPixmap *px, QColor colour)
{
    QImage image(px->toImage());

    for(int i = 0; i < image.width(); i++)
    {
        for(int j = 0; j < image.height(); j++)
        {
            if (((QColor) image.pixel(i,j)) == colour)
            {
                image.setPixel(i,j,Qt::transparent);
            }
        }
    }
    *px = QPixmap::fromImage(image);
    return px;
}

void TileDesignerScene::create(QColor colour, qint32 tileX, qint32 tileY, qint32 sizeX, qint32 sizeY, QString tileSetFilename)
{
    if (!QFile::exists(tileSetFilename))
    {
        QMessageBox::warning(NULL, "Error", "Tile set file was not found");
        return;
    }

    tileSet = new QPixmap(tileSetFilename);
    setTransparentColour(tileSet, colour);

    defaultColour = colour;
    tileSizeX = tileX;
    tileSizeY = tileY;
    mapSizeX = sizeX;
    mapSizeY = sizeY;
    editing = true;

    setSceneRect(0, 0, mapSizeX * tileSizeX, mapSizeY * tileSizeY);

    layers = new QList<Layer>;
    addLayer("hidden");
    addLayer("Background");
}

void TileDesignerScene::addLayer(QString name)
{
    Layer layer(name, true, mapSizeX, mapSizeY);
    layers->append(layer);
}

void TileDesignerScene::setLayerVisible(qint16 layer, bool value)
{
    if (editing && layers->count() > layer)
    {
        (*layers)[layer].setVisible(value);
        invalidate(QRectF(), BackgroundLayer);
    }
}

TileDesignerScene::~TileDesignerScene()
{
    if (tileSet != 0)
    {
        //delete tileSet;
    }
}
